namespace SM2Console
{
    using System;
    using System.Text;
    using Org.BouncyCastle.Asn1.GM;
    using Org.BouncyCastle.Asn1.X9;
    using Org.BouncyCastle.Crypto;
    using Org.BouncyCastle.Crypto.Digests;
    using Org.BouncyCastle.Crypto.Engines;
    using Org.BouncyCastle.Crypto.Generators;
    using Org.BouncyCastle.Crypto.Parameters;
    using Org.BouncyCastle.Math;
    using Org.BouncyCastle.Math.EC;
    using Org.BouncyCastle.Security;
    using Org.BouncyCastle.Utilities.Encoders;

    public class Sm2Util
    {
        private static readonly X9ECParameters x9ECParameters = GMNamedCurves.GetByName("sm2p256v1");

        private static readonly ECDomainParameters ecDomainParameters = new ECDomainParameters(
            x9ECParameters.Curve, x9ECParameters.G, x9ECParameters.N, x9ECParameters.H);

        /// <summary>
        /// SM2加密（完全兼容Java Hutool版本）
        /// </summary>
        public static string Encrypt(string text, string publicKey)
        {
            try
            {
                // 处理公钥格式（如果长度为130，去掉前2个字节）
                if (publicKey.Length == 130)
                {
                    publicKey = publicKey.Substring(2);
                }

                if (publicKey.Length != 128)
                {
                    throw new ArgumentException("公钥长度必须为128位(64字节)");
                }

                // 提取X和Y坐标
                string xhex = publicKey.Substring(0, 64);
                string yhex = publicKey.Substring(64, 64);

                // 创建公钥参数
                ECPublicKeyParameters publicKeyParameters = CreatePublicKeyParameters(xhex, yhex);

                // 使用BouncyCastle的SM2Engine，模式为C1C3C2
                SM2Engine sm2Engine = new SM2Engine(new SM3Digest(), SM2Engine.Mode.C1C3C2);
                sm2Engine.Init(true, new ParametersWithRandom(publicKeyParameters, new SecureRandom()));

                byte[] input = Encoding.UTF8.GetBytes(text);
                byte[] encrypted = sm2Engine.ProcessBlock(input, 0, input.Length);

                // 转换为BCD格式（16进制大写字符串）
                return Remove04Prefix(Hex.ToHexString(encrypted).ToUpper());
            }
            catch (Exception ex)
            {
                throw new Exception("SM2加密失败: " + ex.Message, ex);
            }
        }

        /// <summary>
        /// SM2解密（完全兼容Java Hutool版本）
        /// </summary>
        public static string Decrypt(string text, string privateKey)
        {
            try
            {
                // 创建私钥参数
                ECPrivateKeyParameters privateKeyParameters = CreatePrivateKeyParameters(privateKey);

                // 使用BouncyCastle的SM2Engine，模式为C1C3C2
                SM2Engine sm2Engine = new SM2Engine(new SM3Digest(), SM2Engine.Mode.C1C3C2);
                sm2Engine.Init(false, privateKeyParameters);

                // 将BCD字符串转换为字节数组
                byte[] encrypted = Hex.Decode(Add04PrefixIfNeeded(text));

                // 检查数据并可能添加04前缀（与Java版本兼容）
                encrypted = FixCipherText(encrypted);

                // 解密数据
                byte[] decrypted = sm2Engine.ProcessBlock(encrypted, 0, encrypted.Length);

                return Encoding.UTF8.GetString(decrypted);
            }
            catch (Exception ex)
            {
                throw new Exception("SM2解密失败: " + ex.Message, ex);
            }
        }

        /// <summary>
        /// 修复密文格式（兼容Java版本的处理逻辑）
        /// </summary>
        private static byte[] FixCipherText(byte[] data)
        {
            if (data.Length > 1 && data[0] != 0x04 && data[0] != 0x02 && data[0] != 0x03)
            {
                // 检查数据，如果第一个字节不是04、02、03，则添加04前缀
                byte[] fixedData = new byte[data.Length + 1];
                fixedData[0] = 0x04;
                Array.Copy(data, 0, fixedData, 1, data.Length);
                return fixedData;
            }

            return data;
        }

        /// <summary>
        /// 创建公钥参数
        /// </summary>
        private static ECPublicKeyParameters CreatePublicKeyParameters(string xhex, string yhex)
        {
            try
            {
                BigInteger x = new BigInteger(xhex, 16);
                BigInteger y = new BigInteger(yhex, 16);
                ECPoint q = x9ECParameters.Curve.CreatePoint(x, y);
                return new ECPublicKeyParameters(q, ecDomainParameters);
            }
            catch (Exception ex)
            {
                throw new Exception("创建公钥参数失败: " + ex.Message, ex);
            }
        }

        /// <summary>
        /// 创建私钥参数
        /// </summary>
        private static ECPrivateKeyParameters CreatePrivateKeyParameters(string privateKey)
        {
            try
            {
                BigInteger d = new BigInteger(privateKey, 16);
                return new ECPrivateKeyParameters(d, ecDomainParameters);
            }
            catch (Exception ex)
            {
                throw new Exception("创建私钥参数失败: " + ex.Message, ex);
            }
        }

        private static string Remove04Prefix(String hex) {
            return hex.StartsWith("04") ? hex.Substring(2) : hex;
        }
        
        private static string Add04PrefixIfNeeded(String hex) {
            return hex.StartsWith("04") ? hex : "04" + hex;
        }
        
        /// <summary>
        /// 生成SM2密钥对
        /// </summary>
        public static KeyPair GenerateKeyPair()
        {
            try
            {
                ECKeyPairGenerator generator = new ECKeyPairGenerator();
                generator.Init(new ECKeyGenerationParameters(ecDomainParameters, new SecureRandom()));

                AsymmetricCipherKeyPair keyPair = generator.GenerateKeyPair();

                // 私钥
                ECPrivateKeyParameters privateKey = (ECPrivateKeyParameters)keyPair.Private;
                string privateKeyHex = privateKey.D.ToString(16).PadLeft(64, '0');

                // 公钥
                ECPublicKeyParameters publicKey = (ECPublicKeyParameters)keyPair.Public;
                ECPoint publicPoint = publicKey.Q;
                string xhex = publicPoint.AffineXCoord.ToBigInteger().ToString(16).PadLeft(64, '0');
                string yhex = publicPoint.AffineYCoord.ToBigInteger().ToString(16).PadLeft(64, '0');
                string publicKeyHex = "04" + xhex + yhex;

                return new KeyPair(privateKeyHex, publicKeyHex);
            }
            catch (Exception ex)
            {
                throw new Exception("生成密钥对失败: " + ex.Message, ex);
            }
        }
        
        public class KeyPair
        {
            public KeyPair(string privateKey, string publicKey)
            {
                PrivateKey = privateKey;
                PublicKey = publicKey;
            }

            public string PrivateKey { get; set; }
            public string PublicKey{ get; set; }
        }
    }
}